import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { isNull, isNumber, isObject, isString, isArray } from "lodash";

type Instance = AxiosInstance & {
	[K: string]: any;
};

export type RequestResultType<T> = {
	code: number;
	data: T;
};

const instance: Instance = axios.create({
	timeout: 12000,
	withCredentials: true,
	baseURL: "",
	headers: {
		"Content-Type": "application/json",
		"X-Requested-With": "XMLHttpRequest",
	},
});

type MappingType = {
	alias: string;
	format: (val: string | number | null) => number | string;
	[p: string]: any;
};

/**
 * @description 统一处理请求并返回结果
 *
 * @param {String} url
 * @param {Object} params
 * @param {String} type
 *
 * @param {Object | Array} mapping
 * @return {Promise}
 * */
const returnRequest = (
	url: string,
	params = {},
	type: string,
	mapping: any
): Promise<RequestResultType<any>> => {
	/**
	 * 根据指定的映射对象对输入值进行格式化
	 *
	 * 此函数旨在递归地格式化输入值（val）中的每个属性，根据映射对象（mapping）中定义的规则
	 * 映射对象包含如何格式化每个属性的说明，包括格式化函数、别名和是否覆盖原始属性
	 *
	 * @param val 要格式化的输入值，可以是对象或数组
	 * @param mapping 包含格式化规则的映射对象
	 * @returns 返回格式化后的输入值
	 * @throws 如果映射对象不是有效的对象，抛出错误
	 */
	const formatter = (val: Record<string, any>, mapping: MappingType) => {
		// 检查映射对象是否为有效对象，否则抛出错误
		if (typeof mapping !== "object" || mapping === null) {
			throw new Error("Invalid mapping object");
		}

		// 如果输入值是数组且非空，递归地格式化数组中的每个元素
		if (isArray(val) && val.length > 0) {
			val.forEach((o, i) => {
				val[i] = formatter(o, mapping);
			});
			return val;
		}

		// 如果输入值是对象且非空，根据映射对象中的规则格式化每个属性
		if (isObject(val) && Object.keys(val).length > 0) {
			Object.keys(mapping).forEach((key: string) => {
				const { format, alias, coverage } = mapping[key];

				// 如果属性值是字符串、数字或null，应用格式化函数或直接使用原始值
				if (isString(val[key]) || isNumber(val[key]) || isNull(val[key])) {
					val[alias] = format ? format(val[key], val) : val[key];
					// 如果设置了覆盖标志，删除原始属性
					if (coverage) delete val[key];
				}
				// 如果属性值是对象，递归地应用格式化
				if (isObject(val[key])) {
					return formatter(val[key], mapping[key]);
				}
			});
		}
		return val;
	};

	return new Promise((resolve, reject) => {
		instance[type](url, params)
			.then((r: AxiosResponse) => {
				/**
				 * @description 如果请求响应完成以后需要处理数据
				 *              如果 key 在mapping 里面声明了则取用 mapping的方法对数据进行格式化额外新增一个字段返回
				 * */
				if (r.status === 200 && r.data?.data && isObject(r.data?.data)) {
					r.data.data = formatter(r.data.data, mapping);
				}
				resolve(r.data);
			})
			.catch((r: AxiosResponse) => reject(r));
	});
};

/**
 * @description 请求前拦截
 * */
instance.interceptors.request.use(
	(config: any) => {
		/**
		 * @description 特殊需求在请求体中添加token字段
		 * */
		return config;
	},

	(error: AxiosRequestConfig<any>) => {
		return Promise.reject(error);
	}
);

/**
 * @description 响应前拦截
 * */
instance.interceptors.response.use(
	(request: AxiosResponse) => {
		return request;
	},

	(error: AxiosRequestConfig) => {
		return Promise.reject(error);
	}
);

/**
 * @description GET 请求并返回请求
 * */
const GET = (url: string, params = {}, mapping = {}) =>
	returnRequest(url, { params }, "get", mapping);

/**
 * @description POST 请求并返回请求
 * */
const POST = (url: string, params = {}, mapping = {}) =>
	returnRequest(url, params, "post", mapping);

export { instance, GET, POST };
